﻿using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Routing;
using SsoaFramework.Core.Other;
using System.Linq;

namespace SsoaFramework.Core
{
    public class RoutingTable : IExtension<ServiceHostBase>
    {
        private readonly RoutingConfiguration table = new RoutingConfiguration();
        private ServiceHostBase owner;
        private readonly EnterpriseServiceBus esb;

        #region IExtension<ServiceHostBase> Members
        public void Attach(ServiceHostBase owner)
        {
            this.owner = owner;
        }

        public void Detach(ServiceHostBase owner)
        {
        }
        #endregion

        private List<Service> routedServices = new List<Service>();
        private ServiceRegistry serviceRegistry;

        public RoutingTable(ServiceRegistry ServiceRegistry, EnterpriseServiceBus esb)
        {
            this.esb = esb;
            serviceRegistry = ServiceRegistry;
        }

        public void Route(Service service)
        {
            if (service.MetadataEndpoint == null)
                service = serviceRegistry.First(s => s.ServicePath == service.ServicePath);
            Log.Instance.Info("Adding {0} service to routing table on {1}", service.Name, service.RoutingServicePath);

            table.FilterTable.Add(new EndpointAddressMessageFilter(new EndpointAddress(service.RoutingMetadataPath)), new[] { service.MetadataEndpoint }, 2);
            table.FilterTable.Add(new EndpointAddressMessageFilter(new EndpointAddress(service.RoutingServicePath)), new[] { service.ServiceEndpoint }, 1);

            owner.Extensions.Find<RoutingExtension>().ApplyConfiguration(table);

            routedServices.Add(service);
            service.IsRouted = true;
        }

        public void Unroute(Service service)
        {
            if (service.MetadataEndpoint == null)
                service = serviceRegistry.First(s => s.ServicePath == service.ServicePath);
            Log.Instance.Info("Removing {0} service from routing table", service.Name);

            var filterMetadata = table.FilterTable.Where(f => f.Value.Any(s => s.Address == service.MetadataEndpoint.Address));
            var filterService = table.FilterTable.Where(f => f.Value.Any(s => s.Address == service.ServiceEndpoint.Address));

            try
            {
                table.FilterTable.Remove(filterMetadata.First());
                table.FilterTable.Remove(filterService.First());
            }
            catch (Exception ex)
            {
                Log.Instance.Error("Service {0} is not routed!", service.Name);
            }

            owner.Extensions.Find<RoutingExtension>().ApplyConfiguration(table);

            routedServices.Remove(service);
            service.IsRouted = false;
        }

        public void ChangeRoute(Service service, string path)
        {
            if (service.MetadataEndpoint == null)
                service = serviceRegistry.First(s => s.ServicePath == service.ServicePath);

            var filterService = table.FilterTable.Where(f => f.Value.Any(s => s.Address == service.ServiceEndpoint.Address)).First();

            table.FilterTable.Remove(filterService);
            service.RoutingServicePath = new Uri(esb.ServiceRouter.ServicesAddress + path);
            table.FilterTable.Add(new EndpointAddressMessageFilter(new EndpointAddress(service.RoutingServicePath)), new[] { service.ServiceEndpoint }, 1);
            owner.Extensions.Find<RoutingExtension>().ApplyConfiguration(table);

            Log.Instance.Info("Changing {0} route to {1}", service.Name, service.RoutingServicePath);
        }

        public List<Service> GetRoutedServices()
        {
            return routedServices;
        }
    }
}

